import json
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from collections import defaultdict
import re
from flask import Flask, request, jsonify


class ProductSearchEngine:
    def __init__(self):
        self.products = []
        self.users = []
        self.actions = []
        self.user_item_matrix = None
        self.item_similarity = None
        self.user_similarity = None
        self.product_index = {}
        self.category_index = defaultdict(list)
        self.brand_index = defaultdict(list)
        self.name_index = {}

    def load_data(self, products_file, users_file, actions_file):
        # 加载商品数据
        with open(products_file, 'r', encoding='utf-8') as f:
            self.products = json.load(f)
            for product in self.products:
                self.product_index[product['id']] = product
                self.category_index[product['category']].append(product['id'])
                self.brand_index[product['brand']].append(product['id'])
                # 简单的文本索引
                tokens = re.findall(r'\w+', product['name'].lower())
                for token in tokens:
                    if token not in self.name_index:
                        self.name_index[token] = set()
                    self.name_index[token].add(product['id'])

        # 加载用户数据
        with open(users_file, 'r', encoding='utf-8') as f:
            self.users = json.load(f)

        # 加载行为数据
        with open(actions_file, 'r', encoding='utf-8') as f:
            self.actions = json.load(f)

        # 构建用户-商品交互矩阵
        self._build_user_item_matrix()

        # 计算相似度矩阵
        self._compute_similarities()

    def _build_user_item_matrix(self):
        # 创建用户-商品矩阵
        max_user_id = max([user['id'] for user in self.users])
        max_product_id = max([product['id'] for product in self.products])

        # 初始化矩阵，行为用户，列为商品
        self.user_item_matrix = np.zeros((max_user_id + 1, max_product_id + 1))

        # 填充矩阵，不同行为赋予不同权重
        action_weights = {"view": 1, "collect": 3, "buy": 5}
        for action in self.actions:
            user_id = action['user_id']
            product_id = action['product_id']
            action_type = action['action_type']
            self.user_item_matrix[user_id, product_id] += action_weights.get(action_type, 1)

    def _compute_similarities(self):
        # 计算商品相似度矩阵（基于用户行为）
        self.item_similarity = cosine_similarity(self.user_item_matrix.T)

        # 计算用户相似度矩阵
        self.user_similarity = cosine_similarity(self.user_item_matrix)

    def search(self, query, user_id=None, category=None, brand=None, min_price=None, max_price=None,
               sort_by="relevance", limit=20):
        # 基础查询结果
        result_ids = set()

        # 解析查询词
        tokens = re.findall(r'\w+', query.lower())

        # 基于名称的文本搜索
        if tokens:
            for token in tokens:
                if token in self.name_index:
                    if not result_ids:
                        result_ids = set(self.name_index[token])
                    else:
                        result_ids.intersection_update(self.name_index[token])

        # 基于类别的筛选
        if category and category in self.category_index:
            category_products = set(self.category_index[category])
            if result_ids:
                result_ids.intersection_update(category_products)
            else:
                result_ids = category_products

        # 基于品牌的筛选
        if brand and brand in self.brand_index:
            brand_products = set(self.brand_index[brand])
            if result_ids:
                result_ids.intersection_update(brand_products)
            else:
                result_ids = brand_products

        # 基于价格的筛选
        if min_price or max_price:
            price_filtered = set()
            for product_id in result_ids:
                price = self.product_index[product_id]['price']
                if (min_price is None or price >= min_price) and (max_price is None or price <= max_price):
                    price_filtered.add(product_id)
            result_ids = price_filtered

        # 转为列表
        result_products = [self.product_index[pid] for pid in result_ids]

        # 排序
        if sort_by == "price_asc":
            result_products.sort(key=lambda x: x['price'])
        elif sort_by == "price_desc":
            result_products.sort(key=lambda x: x['price'], reverse=True)
        elif sort_by == "rating":
            result_products.sort(key=lambda x: x['rating'], reverse=True)
        elif sort_by == "sales":
            result_products.sort(key=lambda x: x['sales'], reverse=True)

        # 应用协同过滤推荐（如果有用户ID）
        if user_id and user_id <= len(self.users) and self.user_item_matrix[user_id].sum() > 0:
            # 基于用户的协同过滤
            similar_users = np.argsort(self.user_similarity[user_id])[::-1][1:11]  # 找到最相似的10个用户

            # 收集相似用户喜欢的商品
            cf_recommendations = set()
            for similar_user in similar_users:
                items = np.where(self.user_item_matrix[similar_user] > 0)[0]
                cf_recommendations.update(items)

            # 排除用户已经交互过的商品
            user_items = np.where(self.user_item_matrix[user_id] > 0)[0]
            cf_recommendations = cf_recommendations - set(user_items)

            # 转换为商品对象
            cf_products = [self.product_index[pid] for pid in cf_recommendations if pid in self.product_index]

            # 按评分排序
            cf_products.sort(key=lambda x: x['rating'], reverse=True)

            # 合并搜索结果和推荐结果
            result_products = cf_products[:limit // 2] + result_products
            # 去重并截断
            unique_products = []
            seen_ids = set()
            for product in result_products:
                if product['id'] not in seen_ids:
                    seen_ids.add(product['id'])
                    unique_products.append(product)
                    if len(unique_products) >= limit:
                        break
            result_products = unique_products

        return result_products[:limit]


# 创建Flask应用
app = Flask(__name__)
search_engine = ProductSearchEngine()


@app.before_first_request
def init_search_engine():
    search_engine.load_data("data/products.json", "data/users.json", "data/actions.json")


@app.route('/search', methods=['GET'])
def search_api():
    query = request.args.get('query', '')
    user_id = request.args.get('user_id', type=int)
    category = request.args.get('category')
    brand = request.args.get('brand')
    min_price = request.args.get('min_price', type=float)
    max_price = request.args.get('max_price', type=float)
    sort_by = request.args.get('sort_by', 'relevance')
    limit = request.args.get('limit', 20, type=int)

    results = search_engine.search(
        query=query,
        user_id=user_id,
        category=category,
        brand=brand,
        min_price=min_price,
        max_price=max_price,
        sort_by=sort_by,
        limit=limit
    )

    return jsonify({
        "query": query,
        "results": results,
        "count": len(results)
    })


@app.route('/categories', methods=['GET'])
def get_categories():
    return jsonify(list(search_engine.category_index.keys()))


@app.route('/brands', methods=['GET'])
def get_brands():
    return jsonify(list(search_engine.brand_index.keys()))


if __name__ == '__main__':
    app.run(debug=True)